﻿#include <gui/GuiBase.h>
#include <res/static_resource.h>
#include <imgui_internal.h>

constexpr ImWchar font_ranges[] =
{
	0x0020, 0x00FF, // Basic Latin + Latin Supplement
	0x2000, 0x206F, // General Punctuation
	0x3000, 0x30FF, // CJK Symbols and Punctuations, Hiragana, Katakana
	0x31F0, 0x31FF, // Katakana Phonetic Extensions
	0xFF00, 0xFFEF, // Half-width characters
	0xFFFD, 0xFFFD, // Invalid
	0x4e00, 0x9FAF, // CJK Ideograms
	0,
};

#define ICON_MIN_CI 0xe900
#define ICON_MAX_CI 0xf421

constexpr ImWchar color_ranges[] =
{
	ICON_MIN_CI, ICON_MAX_CI,
	//0xe900,0xe911,
	0,
};

constexpr const char* menu_group_name_mapping[]
{
	u8"文件",
	u8"视图",

};

namespace primal
{
	namespace gui
	{
		struct WindowStorage
		{
			WindowStorage(Window_FN _fn, const char* _name, const char* _icon = nullptr, bool _life = true) :fn{ _fn }, menu_name{ _name }, icon{ _icon }, life{ _life }
			{}
			Window_FN fn;
			const char* menu_name;
			const char* icon;
			bool life;
		};

		struct
		{
			ImGuiContext* context;
			bool desktop_window_visbal;
			ImGuiWindowFlags dock_flags;
			utl::vector<Menu*> menus[MENU_GROUP_MAX];
			utl::vector<GuiWindowBase*> windows;
			utl::vector<WindowStorage> window_fns;
			utl::vector<Menu_FN> menu_fns;
			utl::filebytes font_data;
			utl::filebytes font_color_data;
			ImFont* default_font;
			FontList other_font;
			bool is_init;
		}global{};

		void register_window(Window_FN fn, const char* menu_name, const char* icon)
		{
			global.window_fns.emplace_back(fn, menu_name, icon);
		}

		void register_menu(Menu_FN, const char*, MENU_GROUP)
		{}

		void render()
		{
			impl_new_frame();
			const ImGuiViewport* viewport = ImGui::GetMainViewport();
			ImGui::SetNextWindowPos(viewport->WorkPos);
			ImGui::SetNextWindowSize(viewport->WorkSize);
			ImGui::SetNextWindowViewport(viewport->ID);

			ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
			ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f);
			ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2{});
			ImGui::Begin("Desktop", &global.desktop_window_visbal, global.dock_flags);
			ImGui::PopStyleVar(3);

			if(ImGui::BeginMenuBar())
			{
				//特殊菜单
				if(!global.window_fns.empty() && ImGui::BeginMenu(u8"视图"))
				{
					for(auto& item : global.window_fns)
					{
						if(ImGui::MenuItemEx(item.menu_name, item.icon, nullptr, item.life))
						{
							item.life = !item.life;
						}
					}
					ImGui::EndMenu();
				}
				for(size_t i = 0; i < MENU_GROUP_MAX; i++)
				{
					auto& menu_item{ global.menus[i] };
					if(!menu_item.empty() && ImGui::BeginMenu(menu_group_name_mapping[i]))
					{
						for(auto item : menu_item)
						{
							item->render(global.other_font);
						}
						ImGui::EndMenu();
					}
				}
				ImGui::EndMenuBar();
			}
			ImGui::DockSpace(ImGui::GetID("m"));
			ImGui::End();

			//TODO : after remove
			for(auto& item : global.windows)
			{
				item->render(global.other_font);
			}

			for(auto& item : global.window_fns)
			{
				if(item.life)
				{
					item.fn(global.other_font, &item.life);
				}
			}

			ImGui::ShowDemoWindow();

			ImGui::Render();
			impl_draw();
		}

		bool initialize(GLFWwindow* window)
		{
			if(global.is_init)
			{
				return global.is_init;
			}

			{
				global.context = ImGui::CreateContext();
				ImGui::SetCurrentContext(global.context);
#ifdef NDEBUG
				ImGui::GetIO().IniFilename = nullptr;
				utl::filebytes imgui_config_data{};
				res::get_imgui_config(imgui_config_data);
				ImGui::LoadIniSettingsFromMemory((const char*)imgui_config_data.data(), imgui_config_data.size());
#endif // NDEBUG
				global.dock_flags = ImGuiWindowFlags_MenuBar | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoNavFocus | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoDocking | ImGuiWindowFlags_NoBackground;
				global.desktop_window_visbal = true;
				global.other_font.resize(FontType_Count);
				ImGui::GetIO().ConfigFlags |= ImGuiConfigFlags_DockingEnable;

				primal::res::get_imgui_font(global.font_data);
				primal::res::get_imgui_color_font(global.font_color_data);

				ImFontConfig cfg{};
				cfg.FontDataOwnedByAtlas = false;
				global.default_font = ImGui::GetIO().Fonts->AddFontFromMemoryTTF(global.font_data.data(), global.font_data.size(), 18.0f, &cfg, font_ranges);
				//global.other_font[FontType_Font_Max] = ImGui::GetIO().Fonts->AddFontFromMemoryTTF(global.font_data.data(), global.font_data.size(), 40.0f, &cfg, font_ranges);

				ImGui::GetIO().FontDefault = global.default_font;

				ImFontConfig color_cfg{};
				color_cfg.FontDataOwnedByAtlas = false;
				color_cfg.MergeMode = true;
				global.other_font[FontType_Min] = ImGui::GetIO().Fonts->AddFontFromMemoryTTF(global.font_color_data.data(), global.font_color_data.size(), 18.0f, &color_cfg, color_ranges);
				//global.other_font[FontType_Max] = ImGui::GetIO().Fonts->AddFontFromMemoryTTF(global.font_color_data.data(), global.font_color_data.size(), 40.0f, &color_cfg, color_ranges);
				ImGui::GetIO().Fonts->Build();
				impl_initialize(window);
			}

			global.is_init = true;
			return global.is_init;
		}

		void gui_shutdown()
		{
			gui_impl_shutdown();
			ImGui::DestroyContext(global.context);
		}
	}
}

primal::GuiWindowBase::GuiWindowBase(const std::string& tit, bool default_show) : title{ tit }, is_show{ default_show }, menu{ tit }
{
	menu.binding_window(&this->is_show);
	primal::gui::global.windows.push_back(this);
}

primal::GuiWindowBase::~GuiWindowBase()
{
	utl::vector_delete_element(primal::gui::global.windows, this);
}

void primal::GuiWindowBase::render(const FontList& icon_ft)
{
	if(is_show)
	{
		ImGui::PushID(this);
		if(ImGui::Begin(this->title.c_str(), &is_show))
		{
			this->content(icon_ft);
		}
		ImGui::End();
		ImGui::PopID();
	}
}

primal::Menu::Menu(const std::string& t, MENU_GROUP mg) :title{ t }, group{ mg }
{
	primal::gui::global.menus[mg].push_back(this);
}

primal::Menu::~Menu()
{
	utl::vector_delete_element(primal::gui::global.menus[this->group], this);
}

//primal::MENU_GROUP primal::Menu::get_group() { return this->group; }

primal::WindowViewMenu::WindowViewMenu(const std::string& t) :Menu{ t,MENU_GROUP_VIEW }, window_life{}
{}

#include <res/icon_font_def_u8.h>

void primal::WindowViewMenu::render(const FontList& fts)
{
	assert(window_life != nullptr);
	ImGui::PushFont(fts[primal::FontType::FontType_Min]);
	ImGui::Text(primal::iconft::e921);
	ImGui::PopFont();
	ImGui::SameLine();
	ImGui::MenuItem(this->title.c_str(), nullptr, window_life);
}

void primal::WindowViewMenu::binding_window(bool* wl) { this->window_life = wl; }

bool primal::ImGuiEx::InputWindow(const char* title, input_buffer& out_buf, bool fo)
{
	bool res{};

	if(fo)
	{
		ImVec2 center = ImGui::GetMainViewport()->GetCenter();
		ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
		ImGui::OpenPopup(title);
	}

	if(ImGui::BeginPopupModal(title))
	{
		ImGui::InputText(title, out_buf.data(), out_buf.size());
		if(ImGui::Button(u8"确认", ImVec2(120, 0))) { ImGui::CloseCurrentPopup(); res = true; }
		ImGui::SetItemDefaultFocus();
		ImGui::SameLine();
		if(ImGui::Button(u8"取消", ImVec2(120, 0))) { ImGui::CloseCurrentPopup(); }
		ImGui::EndPopup();
	}

	return res;
}

void primal::ImGuiEx::DrawVec3(const char* lable, float* values, float reset_value, float columnWidth)
{
	constexpr static struct
	{
		ImVec4 color;
		ImVec4 h_color;
		ImVec4 a_color;
		char axis_name[2];
	}
	axies[3]
	{
		{ { 0.8f, 0.1f , 0.15f, 1.0f }, {0.9f, 0.2f , 0.2f, 1.0f }, {0.8f, 0.1f , 0.15f, 1.0f }, {'X',{}} },
		{ { 0.2f, 0.7f , 0.2f , 1.0f }, {0.3f, 0.8f , 0.3f, 1.0f }, {0.2f, 0.7f , 0.2f , 1.0f }, {'Y',{}} },
		{ { 0.1f, 0.25f, 0.8f , 1.0f }, {0.2f, 0.35f, 0.9f, 1.0f }, {0.1f, 0.25f, 0.8f , 1.0f }, {'Z',{}} },
	};


	constexpr ImVec2 button_size{ 30.0f,.0f };

	ImGui::PushID(lable);

	auto col_count = 4;
	ImGui::PushMultiItemsWidths(col_count, ImGui::CalcItemWidth());

	//ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2{ 0, 0 });

	//float lineHeight = GImGui->Font->FontSize + GImGui->Style.FramePadding.y * 2.0f;
	//ImVec2 buttonSize = { lineHeight + 3.0f, lineHeight };

	auto current_value = values;

	for(auto& item : axies)
	{
		ImGui::PushStyleColor(ImGuiCol_Button, item.color);
		ImGui::PushStyleColor(ImGuiCol_ButtonHovered, item.h_color);
		ImGui::PushStyleColor(ImGuiCol_ButtonActive, item.a_color);
		if(ImGui::Button(item.axis_name, button_size))
			(*current_value) = reset_value;
		ImGui::PopStyleColor(3);

		ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2{ 0, 0 });
		ImGui::SameLine();
		ImGui::PushID(current_value);
		ImGui::DragFloat("", current_value, 0.01f, 0.0f, 0.0f, "%.2f");
		ImGui::PopID();
		ImGui::PopStyleVar();
		ImGui::PopItemWidth();
		ImGui::SameLine();
		++current_value;
	}

	ImGui::Text(lable);
	ImGui::PopItemWidth();

	ImGui::PopID();
}
